//
//  YMSG9Packet.m
//  HelloWorld
// *********************************************************************
// This class is nothing more than a convenient data structure to hold
// the information extracted from a single YMSG packet (message).  The
// body array holds the list of strings, in sequence, as they appeared
// in the body section of the packet.
//
// This class is returned by YMSG9InputStreamReader.readPacket();
// See YMSG9InputStream.java for more details on the protocol.
//
// Note: the term 'packet' here is strictly speaking incorrect, as a
// YMSG message could in theory take up more than one TCP packet - but
// it helps to distinguish these lower-level network messages from
// the higher-level dialogue 'message's in the protocol.
// *********************************************************************

//  Created by Martin Le C. on 5/14/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "YMSG9Packet.h"


@implementation YMSG9Packet
@synthesize magic;
@synthesize version;
@synthesize length;
@synthesize service;
@synthesize status;
@synthesize sessionId;
@synthesize body;
// Returns the *key index* (not value index) of n'th key of type k
-(id)init {
	self.body = [[NSMutableArray alloc] init];
	return self;
}
-(NSInteger) getNthLocation:(NSString*)k n:(NSInteger)n {
	for(NSInteger i=0;i<[self.body count];i+=2) {	
		if([[self.body objectAtIndex:i] isEqualToString:k]) n--;
		if(n<0)  return i;
	}
	return -1;
}
-(NSString*) getNthValue:(NSString*)k n:(NSInteger)n {	
	NSInteger l = [self getNthLocation:k n:n];
	if(l<0)  
		return nil;  
	else  
		return [self.body objectAtIndex:(l+1)];
}
-(NSString*) getValue:(NSString*)k {
	return [self getNthValue:k n:0];
}
-(NSMutableArray*) getValues:(NSString*)k {
	NSInteger cnt=0;
	for(int i=0;i<[self.body count];i+=2)
		if([[self.body objectAtIndex:i] isEqualToString:k])  cnt++;
	NSMutableArray* sa = [NSMutableArray arrayWithCapacity:cnt];
	for(int i=0;i<[self.body count];i+=2)
		if([[self.body objectAtIndex:i] isEqualToString:k])  
			[sa addObject:[self.body objectAtIndex:(i+1)]];
	return sa;
}
-(NSString*) getValueFromNthSet: (NSString*)set k:(NSString*)k n:(NSInteger)n {	
	NSInteger i = [self getNthLocation:set n:n];
	if(i<0)  
		return nil;
	i+=2;
	while(i<[self.body count]) {	
		if([[self.body objectAtIndex:i] isEqualToString:k])  
			return [self.body objectAtIndex:(i+1)];	// Found it
		else if([[self.body objectAtIndex:i] isEqualToString:set])  
			return nil;	// Start of next set
		else  
			i+=2;	
	}
	return nil;
}
-(BOOL)exists:(NSString*)k {
	return ([self getValue:k] !=nil);
}
// FIX: Not thread safe (reading array while copies are taking place)
-(void)append: (YMSG9Packet*)pkt {
	NSMutableArray* arr = [NSMutableArray arrayWithCapacity:[self.body count] + [pkt.body count]];
	[arr addObjectsFromArray: (NSArray*)[self body]];
	[arr addObjectsFromArray: (NSArray*)[pkt body]];
	self.body = arr;
	[arr release];
				
}
// Merge the supplied packet into this one, with special regard to 
// certain fields which need to be concatenated.  If the field key is 
// in the array provided, it is appended onto the end of the current value 
// for that key (if it doesn't exist it is created).  Other keys are
// appended to the end of this packet.
// FIX: Not thread safe (reading array while copies are taking place)
-(void)merge:(YMSG9Packet*) pkt concatFields:(NSMutableArray*) concatFields {
	NSMutableArray* appendBuffer = [NSMutableArray array];
	for(int i=0;i<[pkt.body count];i+=2)
	{	// -----Get next key/value
		NSString* k=[pkt.body objectAtIndex:i];
		NSString* v=[pkt.body objectAtIndex:(i+1)];
		// -----Look for key in list of merging fields
		BOOL b = NO;
		for(NSInteger j=0;j<[concatFields count];j++) {
			if([[concatFields objectAtIndex:j] isEqualToString:k]) { 
				b = YES;  
				break;
			}
		}
		// -----Its on the list, so attempt to merge to current
		if(b)
		{	
			NSInteger idx = [self getNthLocation:k n:0];
			// -----If may be on the list, but do we have a field like this already?
			if(idx<0)
			{	// -----No!  Forget about the merge, just append to list
				// FIX: what happens if two such fields appear in the same
				// packet - one will overwrite the other?  (Can this happen?)
				[appendBuffer addObject:k];
				[appendBuffer addObject:[pkt.body objectAtIndex:(i+1)]];
			}
			else
			{	// -----Yes!  Merge!
				[[self.body objectAtIndex:(idx+1)] stringByAppendingString:v];
			}
		}
		else
		{	// -----Append new body field to current body field
			[appendBuffer addObject:k];
			[appendBuffer addObject:[pkt.body objectAtIndex:(i+1)]];
		}
	}
	if([appendBuffer count]>0)
	{	
		NSMutableArray* arr = [NSMutableArray arrayWithCapacity:([self.body count]+[appendBuffer count])];
		[arr addObjectsFromArray:(NSArray*)self.body];
		for(NSInteger i=0;i<[appendBuffer count];i++)
			[arr addObject:[appendBuffer objectAtIndex:i]];
		self.body = arr;
		[arr release];
		[appendBuffer release];

	}
				
}
-(NSString*)toString {
	NSString* s= [NSString stringWithFormat:@"Magic:%@ Version:%d Length:%d Service:%x Status:%qi SessionId:0x%qx \n ", self.magic, self.version, self.length, self.service, self.status, self.sessionId];
	for(NSInteger i=0;i<[self.body count];i++) {
		[s stringByAppendingString:@" ["];
		[s stringByAppendingString:[self.body objectAtIndex:i]];
		[s stringByAppendingString:@"]"];
	}
	return s;
}
@end